// @ts-check
import { actions } from './store';
/**
 * @typedef {import('@reduxjs/toolkit').ThunkAction<unknown,unknown,unknown,any>} AppThunk
 * @typedef {(...params)=>AppThunk} ThunkFn
 */

async function wait(time) {
  return new Promise((resolve) => setTimeout(resolve, time));
}
/**@type {ThunkFn} */
const subA = (name) => async (dispatch, getState) => {
  await wait(500);
  getState();
  dispatch(actions.add('A:---(1)-' + name));
  await wait(800);
  getState();
  dispatch(actions.add('A:---(2)-' + name));
  await wait(1000);
  getState();
  dispatch(actions.add('A:---(3)-' + name));
  await wait(1000);
};

/**@type {ThunkFn} */
const _A =
  (name = Date.now().toString(20).substring(6, 10)) =>
  async (dispatch, getState) => {
    getState();
    dispatch(actions.add('A start:' + name));
    await subA(name)(dispatch, getState, {});
    // await dispatch(subA(name)); // 不要这样调用！这将导致传入subA的dispatch回退为redux自身的了，失去了abort包装
    getState();
    dispatch(actions.add('A end:' + name));
  };

/**@type {ThunkFn} */
const _B =
  (name = Date.now().toString(20).substring(6, 10)) =>
  async (dispatch, getState) => {
    getState();
    dispatch(actions.add('B start:' + name));
    await wait(500);
    getState();
    dispatch(actions.add('B:===(1)-' + name));
    await wait(800);
    getState();
    dispatch(actions.add('B:===(2)-' + name));
    await wait(1000);
    getState();
    dispatch(actions.add('B:===(3)-' + name));
    await wait(1000);
    getState();
    dispatch(actions.add('B end:' + name));
  };

const NotLatestError = new Error('------');

/**
 *
 * @param {ThunkFn} fn
 * @returns {ThunkFn}
 */
const proxyThunkAction = function (fn) {
  let latestKey;
  return function (...params) {
    const key = (latestKey = Symbol());

    const proxyFn = (f) => {
      return (...params) => {
        if (key !== latestKey) {
          throw NotLatestError;
        }
        return f(...params);
      };
    };

    return async function (dispatch, getState, extra) {
      const newDispatch = proxyFn(dispatch);
      const newGetState = proxyFn(getState);

      try {
        return await fn(...params)(newDispatch, newGetState, extra);
      } catch (err) {
        if (err !== NotLatestError) {
          throw err;
        }
      }
    };
  };
};

export const A = proxyThunkAction(_A);
export const B = proxyThunkAction(_B);
